package com.ruoyi.web.utils;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import com.xkzhangsan.time.converter.DateTimeConverterUtil;
import com.xkzhangsan.time.formatter.DateTimeFormatterUtil;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.cert.*;
import java.time.ZonedDateTime;
import java.util.*;

public class CommonUtil {

    static String serialNo;

    /**
     * 获取Ip地址
     * @param request
     * @return
     */
    public static String getIpAddress(HttpServletRequest request) {
        String Xip = request.getHeader("X-Real-IP");
        String XFor = request.getHeader("X-Forwarded-For");
        if(StringUtils.isNotEmpty(XFor) && !"unKnown".equalsIgnoreCase(XFor)){
            //多次反向代理后会有多个ip值，第一个ip才是真实ip
            int index = XFor.indexOf(",");
            if(index != -1){
                return XFor.substring(0,index);
            }else{
                return XFor;
            }
        }
        XFor = Xip;
        if(StringUtils.isNotEmpty(XFor) && !"unKnown".equalsIgnoreCase(XFor)){
            return XFor;
        }
        if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
            XFor = request.getHeader("Proxy-Client-IP");
        }
        if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
            XFor = request.getHeader("WL-Proxy-Client-IP");
        }
        if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
            XFor = request.getHeader("HTTP_CLIENT_IP");
        }
        if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
            XFor = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
            XFor = request.getRemoteAddr();
        }
        System.out.println("当前ip地址: " + XFor);
        return XFor;
    }

    /**
     * 构建参数
     * @param signMessage
     * @return
     */
    public static String buildSignMessage(List<String> signMessage) {
        if (signMessage != null && signMessage.size() > 0) {
            StringBuilder sbf = new StringBuilder();
            Iterator var2 = signMessage.iterator();

            while(var2.hasNext()) {
                String str = (String)var2.next();
                sbf.append(str).append("\n");
            }
            System.out.println("list是：" + sbf.toString());
            return sbf.toString();
        } else {
            return null;
        }
    }

    /**
     * 构建签名且加密
     * @param data
     * @param privateKey
     * @return
     * @throws Exception
     */
    public static String encryptByPrivateKey(String data, PrivateKey privateKey) throws Exception {
        Signature signature = Signature.getInstance("SHA256WithRSA");
        signature.initSign(privateKey);
        signature.update(data.getBytes(StandardCharsets.UTF_8));
        byte[] signed = signature.sign();
        return StrUtil.str(Base64.encode(signed));
    }

    /**
     * 编码
     * @param str
     * @return
     */
    public static String getURLEncoderString(String str) {
        String result = "";
        if (null == str) {
            return "";
        }
        try {
            result = java.net.URLEncoder.encode(str, "GBK");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return result;
    }
    /**
     * 构建签名
     * @param signMessage
     * @param keyPath
     * @return
     * @throws Exception
     */
    public static String createSign(String signMessage, String keyPath) throws Exception {
        if (StrUtil.isEmpty(signMessage)) {
            return null;
        } else {
            PrivateKey privateKey = PemUtil.loadPrivateKey(
                    new FileInputStream(keyPath));
            return encryptByPrivateKey(signMessage, privateKey);
        }
    }

    /**
     * 时间工具,时间戳
     * @param date
     * @return
     * @throws Exception
     */
    public static String dateToTimeZone(Date date) throws Exception {
        if (date == null) {
            throw new Exception("date is not null");
        } else {
            ZonedDateTime zonedDateTime = DateTimeConverterUtil.toZonedDateTime(date);
            String time = DateTimeFormatterUtil.format(zonedDateTime, DateTimeFormatterUtil.YYYY_MM_DD_T_HH_MM_SS_XXX_FMT);
            return time;
        }
    }

    public static String dateToTimeZone(long time) throws Exception {
        return dateToTimeZone(new Date(time));
    }

    /**
     * 构建支付参数，以便前端Js可以jsAPi的唤起微信支付
     * @param appId
     * @param prepayId
     * @param keyPath
     * @return
     * @throws Exception
     */
    public static Map<String, String> buildPayMap(String appId, String prepayId, String keyPath) throws Exception{
        String timeStamp = String.valueOf(System.currentTimeMillis() / 1000L);
        String nonceStr = String.valueOf(System.currentTimeMillis());
        String packageStr = "prepay_id=" + prepayId;
        Map<String, String> packageParams = new HashMap(6);
        packageParams.put("appId", appId);
        packageParams.put("timeStamp", timeStamp);
        packageParams.put("nonceStr", nonceStr);
        packageParams.put("package", packageStr);
        packageParams.put("signType", "RSA");
        ArrayList<String> list = new ArrayList();
        list.add(appId);
        list.add(timeStamp);
        list.add(nonceStr);
        list.add(packageStr);
        String packageSign = CommonUtil.createSign(CommonUtil.buildSignMessage(list), keyPath);
        packageParams.put("paySign", packageSign);
        return packageParams;
    }

    /**
     * 随机订单号生成
     * @return
     */
    public static String generateStr() {
        return IdUtil.fastSimpleUUID();
    }

    /**
     * 校验签名且解码
     * @param data
     * @param sign
     * @param publicKey
     * @return
     * @throws Exception
     */
    public static boolean checkByPublicKey(String data, String sign, PublicKey publicKey) throws Exception {
        Signature signature = Signature.getInstance("SHA256WithRSA");
        signature.initVerify(publicKey);
        signature.update(data.getBytes(StandardCharsets.UTF_8));
        return signature.verify(Base64.decode(sign.getBytes(StandardCharsets.UTF_8)));
    }

    /**
     * 校验签名
     * @param signature
     * @param body
     * @param nonce
     * @param timestamp
     * @param publicKey
     * @return
     * @throws Exception
     */
    public static boolean verifySignature(String signature, String body, String nonce, String timestamp, PublicKey publicKey) throws Exception {
        List<String> list = new ArrayList<>(3);
        list.add(timestamp);
        list.add(nonce);
        list.add(body);
        String buildSignMessage = buildSignMessage(list);
        return checkByPublicKey(buildSignMessage, signature, publicKey);
    }


    /**
     * 保存平台证书到本地
     * @param apiKey3
     * @param associatedData
     * @param nonce
     * @param cipherText
     * @param certPath
     * @return
     */
    public static String savePlatformCert(String apiKey3, String associatedData, String nonce, String cipherText, String certPath) {
        try {
            AesUtil aesUtil = new AesUtil(apiKey3.getBytes(StandardCharsets.UTF_8));
            // 平台证书密文解密
            // encrypt_certificate 中的  associated_data nonce  ciphertext
            String publicKey = aesUtil.decryptToString(
                    associatedData.getBytes(StandardCharsets.UTF_8),
                    nonce.getBytes(StandardCharsets.UTF_8),
                    cipherText
            );
            // 保存证书
            FileWriter writer = new FileWriter(certPath);
            writer.write(publicKey);
            // 获取平台证书序列号
            X509Certificate certificate = CommonUtil.getCertificate(new ByteArrayInputStream(publicKey.getBytes()));
            return certificate.getSerialNumber().toString(16).toUpperCase();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 获取CA证书的序列号
     * @param certPath
     * @return
     */
    public static String getSerialNumber(String serialNo,String certPath) {
        if (StrUtil.isEmpty(serialNo)) {
            // 获取CA证书序列号
            X509Certificate certificate = CommonUtil.getCertificate(FileUtil.getInputStream(certPath));
            serialNo = certificate.getSerialNumber().toString(16).toUpperCase();

//            System.out.println("输出证书信息:\n" + certificate.toString());
//            // 输出关键信息，截取部分并进行标记
//            System.out.println("证书序列号:" + certificate.getSerialNumber().toString(16));
//            System.out.println("版本号:" + certificate.getVersion());
//            System.out.println("签发者：" + certificate.getIssuerDN());
//            System.out.println("有效起始日期：" + certificate.getNotBefore());
//            System.out.println("有效终止日期：" + certificate.getNotAfter());
//            System.out.println("主体名：" + certificate.getSubjectDN());
//            System.out.println("签名算法：" + certificate.getSigAlgName());
//            System.out.println("签名：" + certificate.getSignature().toString());
        }
        System.out.println("serialNo:" + serialNo);
        return serialNo;
    }

    /**
     * 对微信返回的密文进行解密
     * @param serialNo
     * @param body
     * @param signature
     * @param nonce
     * @param timestamp
     * @param key
     * @param certPath
     * @return
     * @throws Exception
     */
    public static String verifyNotify(String serialNo, String body, String signature, String nonce, String timestamp, String key, String certPath) throws Exception {
        BufferedInputStream inputStream = FileUtil.getInputStream(certPath);
        X509Certificate certificate = getCertificate(inputStream);
        String serialNumber = certificate.getSerialNumber().toString(16).toUpperCase();
        System.out.println(serialNumber);
        if (serialNumber.equals(serialNo)) {
            boolean verifySignature = verifySignature(signature, body, nonce, timestamp, certificate.getPublicKey());
            if (verifySignature) {
                JSONObject resultObject = JSONUtil.parseObj(body);
                JSONObject resource = resultObject.getJSONObject("resource");
                String cipherText = resource.getStr("ciphertext");
                String nonceStr = resource.getStr("nonce");
                String associatedData = resource.getStr("associated_data");
                AesUtil aesUtil = new AesUtil(key.getBytes(StandardCharsets.UTF_8));
                return aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8), nonceStr.getBytes(StandardCharsets.UTF_8), cipherText);
            }
        }

        return null;
    }

    /**
     * 格式化request内容,返回
     * @param request
     * @return
     */
    public static String readData(HttpServletRequest request) {
        BufferedReader br = null;

        try {
            StringBuilder result = new StringBuilder();

            String line;
            for(br = request.getReader(); (line = br.readLine()) != null; result.append(line)) {
                if (result.length() > 0) {
                    result.append("\n");
                }
            }

            line = result.toString();
            return line;
        } catch (IOException var12) {
            throw new RuntimeException(var12);
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException var11) {
                    var11.printStackTrace();
                }
            }

        }
    }


    /**
     * 校验平台证书
     * @param inputStream
     * @return
     */
    public static X509Certificate getCertificate(InputStream inputStream) {
        try {
            CertificateFactory cf = CertificateFactory.getInstance("X509");
            X509Certificate cert = (X509Certificate)cf.generateCertificate(inputStream);
            cert.checkValidity();
            return cert;
        } catch (CertificateExpiredException var3) {
            throw new RuntimeException("证书已过期", var3);
        } catch (CertificateNotYetValidException var4) {
            throw new RuntimeException("证书尚未生效", var4);
        } catch (CertificateException var5) {
            throw new RuntimeException("无效的证书", var5);
        }
    }
}
